#include "TFIDF.h"
#include "Common/FileIO.h"
#include "Common/Attribute.h"
#include <algorithm>

using namespace std;

CTFIDF::CTFIDF()
{
	m_bFlagCreate = false;
}

CTFIDF::~CTFIDF()
{
	if(m_bFlagCreate)
		Destroy();
}

bool CTFIDF::Create(vector<string>& documents)
{
	//if(!pFilename || m_bFlagCreate)
	//	return false; 

	//CFileIO			file;
	char			szBuffer[1024];
	//vector<string>	documents;

	//if(!file.Create(pFilename))
	//	return false;

	// create a dictionary
	m_ptrDictionary = new CDictionary;

	if(!m_ptrDictionary->Create(true))
		return false;

	/*
	while(file.ReadLine(szBuffer))
	{
		documents.push_back(szBuffer);
		m_ptrDictionary->AddSentence(szBuffer);
	}
	*/

	for(int i = 0; i < documents.size(); i++)
		m_ptrDictionary->AddSentence(documents[i]);

	//file.Destroy();

	m_nDocSize = documents.size();
	// term frequency
	for(int i = 0; i < m_nDocSize; i++)
	{
		CAttribute		attr(documents[i], CDictionary::DELIM);
		vector<string>	list = attr.List();

		// count term frequency
		for(int j = 0; j < list.size(); j++)
		{
			int row = m_ptrDictionary->Index(list[j]);
			// (document id, term index)
			m_termFreq(i + 1, row) = m_termFreq(i + 1, row) + 1;
		}
	}

	// inverse docuement frequency
	for(int i = 0; i < m_nDocSize; i++)
	{
		CAttribute		attr(documents[i], CDictionary::DELIM);
		vector<string>	list = attr.List();

		for(int j = 0; j < list.size(); j++)
		{
			int		row		 = m_ptrDictionary->Index(list[j]);
			float	termFreq = (float)(m_ptrDictionary->Frequency(list[j]));
			float	termDoc	 = m_termFreq(i + 1, row);

			m_tfidf(i + 1, row) = (termFreq / termDoc) *
								  log((float)m_nDocSize / t_DocFreq(row));
		}
	}

	t_InitLengthVec();

	m_bFlagCreate = true;

	return true;
}

bool CTFIDF::Destroy()
{
	if(!m_bFlagCreate)
		return false;

	if(m_ptrDictionary)
		delete m_ptrDictionary;

	m_bFlagCreate = false;

	return true;
}

vector<pair<int, float> > CTFIDF::SimilarDocs(string query)
{
	//transform(query.begin(), query.end(), query.begin(), tolower);

	CAttribute				attr(query, CDictionary::DELIM);
	vector<string>			list	= attr.List();
	unordered_map<int, int>	vector;
	float					denom	= 0;
	float					sim		= 0;
	float					lenVec;
	std::vector<pair<int, float> >		simList;

	for(int i = 0; i < list.size(); i++)
	{
		int pos = m_ptrDictionary->Index(list[i]);

		if(pos != -1)
			vector[pos]++;
	}

	lenVec = t_LengthVec(vector);
	// compute cosine similarity
	for(int i = 0; i < m_nDocSize; i++)
	{
		float dicLen = m_dicLength[i];
		denom = lenVec * dicLen;
		
		sim = (denom == 0) ? 0 : t_InnerProduct(vector, i + 1) / denom;

		if(sim > 0)
			simList.push_back(make_pair(i, sim));
	}

	sort(simList.begin(), simList.end(), [](pair<int, float> a, pair<int, float> b)
	{
		return (a.second > b.second);
	});

	return simList;
}

int CTFIDF::SimilarDoc(std::string query)
{
	//transform(query.begin(), query.end(), query.begin(), tolower);

	CAttribute				attr(query, CDictionary::DELIM);
	vector<string>			list	= attr.List();
	unordered_map<int, int>	vector;
	float					denom	= 0;
	float					sim		= 0;
	float					mostSim = 0;
	int						nIndex	= -1;

	for(int i = 0; i < list.size(); i++)
	{
		int pos = m_ptrDictionary->Index(list[i]);

		if(pos != -1)
			vector[pos]++;
	}

	// compute cosine similarity
	for(int i = 0; i < m_nDocSize; i++)
	{
		denom = t_LengthVec(vector) * t_LengthVec(i + 1);
		
		sim = (denom == 0) ? 0 : t_InnerProduct(vector, i + 1) / denom;

		if(sim > mostSim)
		{
			mostSim = sim;
			nIndex = i;
		}
	}

	return nIndex;
}

float CTFIDF::t_InnerProduct(std::unordered_map<int, int> a, int nRow)
{
	float	result = 0;
	int		nSize = m_ptrDictionary->Size();

	for(int i = 0; i < nSize; i++)
		result += a[i] * m_tfidf(nRow, i);

	return result;
}

float CTFIDF::t_LengthVec(std::unordered_map<int, int> a)
{
	float	result = 0;
	int		nSize = m_ptrDictionary->Size();

	for(int i = 0; i < nSize; i++)
		result += a[i] * a[i];

	result = sqrt(result);

	return result;
}

float CTFIDF::t_LengthVec(int nRow)
{
	float	result = 0;
	int		nSize = m_ptrDictionary->Size();

	for(int i = 0; i < nSize; i++)
		result += m_tfidf(nRow, i + 1) * m_tfidf(nRow, i + 1);

	result = sqrt(result);

	return result;
}

int CTFIDF::t_DocFreq(int nIndex)
{
	int nDocCnt  = 0;

	for(int i = 0; i < m_nDocSize; i++)
	{
		if(m_termFreq(i + 1, nIndex) > 0)
			nDocCnt++;
	}

	return nDocCnt;
}

void CTFIDF::t_InitLengthVec()
{
	for(int i = 0; i < m_nDocSize; i++)
		m_dicLength[i] = t_LengthVec(i + 1);

}

